package com.gitee.zhuyunlong2018.mybatisplusrelations.handler;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.gitee.zhuyunlong2018.mybatisplusrelations.config.RelationConfig;
import com.gitee.zhuyunlong2018.mybatisplusrelations.context.IContext;
import com.gitee.zhuyunlong2018.mybatisplusrelations.func.IGetter;
import com.gitee.zhuyunlong2018.mybatisplusrelations.utils.CollectionUtils;
import com.gitee.zhuyunlong2018.mybatisplusrelations.utils.StringUtils;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toSet;

/**
 * list绑定多对多的关系
 *
 * @param <T>
 * @param <R>
 */
public class ListManyBindManyHandler<T, R> extends ListBindManyHandler<T, R> implements IManyBindHandler<T, R> {

    /**
     * 连接表的外键集合
     */
    private Set<?> linkForeignProperties;

    /**
     * 中间表数据list
     */
    private List<?> linkList;

    public ListManyBindManyHandler(IContext<T> context, IGetter<T, ?> propertyGetter) {
        super(context, propertyGetter);
    }

    @Override
    protected LambdaQueryWrapper<R> getQueryWrapper() {
        return getQueryWrapper(linkForeignProperties);
    }

    @SuppressWarnings({"unchecked"})
    @Override
    protected LambdaQueryWrapper<R> getQueryWrapper(Collection<?> inParams) {
        LambdaQueryWrapper<R> wrapper = (LambdaQueryWrapper<R>) Wrappers.lambdaQuery(cache.getForeignEntityClass())
                .in(cache.getForeignPropertyGetter(), inParams);
        wrapper.apply(!StringUtils.isEmpty(cache.getApplySql()), cache.getApplySql());
        if (lambdaWrapperFunc != null) {
            lambdaWrapperFunc.accept(wrapper);
        }
        wrapper.last(!StringUtils.isEmpty(cache.getLastSql()), cache.getLastSql());
        return wrapper;
    }

    @SuppressWarnings({"unchecked"})
    private LambdaQueryWrapper<R> getLinkQueryWrapper(Collection<?> inParams) {
        // 用批量Id查询用户信息
        LambdaQueryWrapper<R> linkWrapper = Wrappers.lambdaQuery(cache.getLinkEntityClass())
                .in(cache.getLinkLocalPropertyGetter(), inParams);
        linkWrapper.apply(!StringUtils.isEmpty(cache.getLinkApplySql()), cache.getLinkApplySql());
        if (linkLambdaWrapperFunc != null) {
            linkLambdaWrapperFunc.accept(linkWrapper);
        }
        linkWrapper.last(!StringUtils.isEmpty(cache.getLinkLastSql()), cache.getLinkLastSql());
        return linkWrapper;
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    @Override
    protected void queryRelation() {
        if (linkList == null) {
            Set<?> localProperties = list.stream()
                    .map(s -> cache.getLocalPropertyGetter().apply(s))
                    .filter(Objects::nonNull)
                    .collect(toSet());
            if (localProperties.isEmpty()) {
                return;
            }
            if (RelationConfig.shouldLoop(localProperties)) {
                Collection<? extends Collection<?>> collections = CollectionUtils.subCollection(localProperties, RelationConfig.LOOP_MAX_IN_PARAMS);
                linkList = new ArrayList<>();
                for (Collection<?> collection : collections) {
                    if (collection.isEmpty()) {
                        break;
                    }
                    linkList.addAll(
                            getLinkModel().selectList((LambdaQueryWrapper) getLinkQueryWrapper(collection))
                    );
                }
            } else {
                linkList = getLinkModel().selectList((LambdaQueryWrapper) getLinkQueryWrapper(localProperties));
            }
        }
        if (streamLinkStatus) {
            // 进行过滤处理
            linkList = getLinkStream(linkList).collect(Collectors.toList());
        }
        // 外键集合ID
        linkForeignProperties = (Set<?>) linkList
                .stream()
                .map(cache.getLinkForeignPropertyGetter())
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (!linkForeignProperties.isEmpty() && foreignEntityList == null) {
            if (RelationConfig.shouldLoop(linkForeignProperties)) {
                // 将关联的in参数进行分割成多个
                Collection<? extends Collection<?>> collections = CollectionUtils.subCollection(linkForeignProperties, RelationConfig.LOOP_MAX_IN_PARAMS);
                foreignEntityList = new ArrayList<>();
                for (Collection<?> collection : collections) {
                    if (collection.isEmpty()) {
                        break;
                    }
                    foreignEntityList.addAll(
                            (List<R>) getForeignModel()
                                    .selectList((LambdaQueryWrapper) getQueryWrapper(collection))
                    );
                }
            } else {
                // 查询关联数据
                foreignEntityList = getForeignModel()
                        .selectList((LambdaQueryWrapper) getQueryWrapper(linkForeignProperties));
            }
            foreignEntityList = covertListModelToVO(foreignEntityList);
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    @Override
    protected void buildRelation() {
        if (linkList.isEmpty() || foreignEntityList == null || foreignEntityList.isEmpty()) {
            return;
        }
        // 中间表hashMap
        HashMap linkMap = (HashMap) linkList.stream()
                .collect(groupingBy(cache.getLinkLocalPropertyGetter()));
        // 关联表hashMap
        HashMap<Object, List<Object>> linkDataMap = (HashMap) foreignEntityList
                .stream()
                .collect(groupingBy(cache.getForeignPropertyGetter()));
        // 组装
        list.forEach(e -> {
            List<Object> linkData = (List) linkMap.get(cache.getLocalPropertyGetter().apply(e));
            if (null == linkData || linkData.isEmpty()) {
                cache.getRelationEntitySetter().accept(e, Collections.emptyList());
                return;
            }
            List<List<R>> subList = (List) linkData.stream()
                    .map(cache.getLinkForeignPropertyGetter())
                    .map(linkDataMap::get)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            List<R> foreignList = subList.stream()
                    .flatMap(List::stream)
                    .collect(Collectors.toList());
            cache.getRelationEntitySetter().accept(e, foreignList);
            if (!StringUtils.isEmpty(cache.getIterateLinkMethod())) {
                // 进入模型迭代器
                List<Object> iterateData = linkData.stream()
                        .filter(
                                linkItem ->
                                        linkDataMap.containsKey(cache.getLinkForeignPropertyGetter().apply(linkItem))
                        )
                        .collect(Collectors.toList());
                cache.getIterateLinkMethodSetter().accept(e, iterateData);
            }
            if (streamStatus) {
                Stream<R> stream = getForeignStream(foreignList);
                cache.getRelationEntitySetter().accept(e, stream.collect(Collectors.toList()));
            }
        });
    }

    /**
     * 中间表查询构造，多对多时才有用，使用方式如下，其中UserSkillRelation为中间表模型
     * .linkQuery((LambdaQueryWrapper<UserSkillRelation> wrapper) -> {
     * wrapper.gt(UserSkillRelation::getScore, 90);
     * })
     *
     * @param lambdaWrapperFunc
     * @param <L>
     * @return
     */
    @SuppressWarnings({"unchecked"})
    public <L extends Model<?>> IManyBindHandler<T, R> linkQuery(Consumer<LambdaQueryWrapper<L>> lambdaWrapperFunc) {
        this.linkLambdaWrapperFunc = (Consumer<LambdaQueryWrapper<?>>) (Object) lambdaWrapperFunc;
        return this;
    }

    @Override
    public <L extends Model<?>> IManyBindHandler<T, R> linkFilter(Predicate<L> streamFilter) {
        streamLinkFilter = streamFilter;
        streamLinkStatus = true;
        return this;
    }

    @Override
    public <L extends Model<?>> IManyBindHandler<T, R> linkSorted(Comparator<L> streamComparator) {
        streamLinkComparator = streamComparator;
        streamLinkStatus = true;
        return this;
    }

    @Override
    public <L> IManyBindHandler<T, R> setLinkData(List<L> linkData) {
        linkList = linkData;
        streamLinkStatus = true;
        return this;
    }
}
